#pragma once

#include <iostream>
#include <fstream>
#include <string>
#include <queue>
#include <unordered_map>
#include <mutex>
#include <thread>
#include <atomic>
#include <memory>
#include <functional>
#include <condition_variable>

#include "../log.hpp"
#include "../util.hpp"
#include "Connection.hpp"

// 实现连接池功能模块
using namespace ns_log;
using namespace ns_util;
class connectionPool
{
public:
    // 获取连接池单例(懒汉模式)
    static connectionPool *getConnectionPool()
    {
        if (nullptr == _conninstance)
        {
            _connMutex.lock();
            if (nullptr == _conninstance)
            {
                _conninstance = new connectionPool();
            }
            _connMutex.unlock();
        }
        return _conninstance;
    }

    std::shared_ptr<connection> getConnection()
    {
        std::unique_lock<std::mutex> lock(_queueMutex);
        while (_connectionQueue.empty())
        {
            // 不能使用 sleep 函数, sleep 是直接休眠
            // 线程向下执行的可能情况: 被唤醒和超时
            // 被唤醒的可能情况: 生产者生产了新连接或消费者归还了连接
            // 如果是被唤醒的话, 队列肯定不为空, 那么就会跳出 while 循环
            // 如果是超时的话, 再判断队列是否为空, 如果为空, 那么获取连接
            // 就失败了; 如果不为空, 那么也会跳出 while 循环
            if (std::cv_status::timeout == _cv.wait_for(lock, std::chrono::microseconds(_connectionTimeout)))
            {
                if (_connectionQueue.empty())
                {
                    return nullptr;
                }
            }
        }

        // 队列不为空
        // shared_ptr 智能指针析构时, 会把 Connection 资源直接 delete掉
        // 相当于调用 Connection 的析构函数, 将 MySQL 连接给关闭掉, 因此
        // 需要定制 shared_ptr 释放资源的方法
        // 定制删除器: 将消费者用完的 Connection 连接放回到队列中
        std::shared_ptr<connection> sp(_connectionQueue.front(), [&](connection *pcon)
                                       {
		// 这里是在服务器应用线程中调用的, 因此一定要考虑队列的线程安全问题
		std::unique_lock<std::mutex> lock(_queueMutex);
		pcon->refreshAliveTime(); // 刷新一下连接开始空闲的起始时间
		_connectionQueue.push(pcon); });
        _connectionQueue.pop(); // 消费者获取了该连接, 因此需要 pop 掉
        if (_connectionQueue.empty())
        {
            // 谁消费了队列中最后一个 Connection, 谁负责通知生产者进行生产
            _cv.notify_all();
        }

        return sp;
    }

private:
    const std::string root_path = "/home/wyy/novel_web/comm/mysql_connection_pool";

    connectionPool()
    {
        if (!loadConfigFile())
        {
            LOG(ERROR) << "加载配置文件失败!"
                       << "\n";
            return;
        }
        LOG(INFO) << "加载配置文件成功!"
                  << "\n";
        for (int i = 0; i < _initSize; ++i)
        {
            connection *con = new connection();
            if (!(con->connect(_ip, _port, _username, _password, _db)))
            {
                LOG(ERROR) << "MySQL连接失败"
                           << "\n";
                return;
            }
            con->refreshAliveTime();
            _connectionQueue.push(con);
            ++_connectionCount;
        }

        std::thread producer(std::bind(&connectionPool::produceConnection, this));
        producer.detach();

        std::thread scanner(std::bind(&connectionPool::scannConnection, this));
        scanner.detach();
    }

    connectionPool(const connectionPool &c) = delete;

    // 加载配置文件
    bool loadConfigFile()
    {
        std::string data;
        std::string path_name = root_path + "/mysql.conf";
        if (!fileUtil::readFile(path_name, &data))
        {
            return false;
        }
        std::unordered_map<std::string, std::string> loginParam;
        std::vector<std::string> loginStr;
        stringUtil::splitString(data, &loginStr, "\n");
        for (auto &s : loginStr)
        {
            std::vector<std::string> kv;
            stringUtil::splitString(s, &kv, "=");
            loginParam[kv[0]] = kv[1];
        }
        _ip = loginParam["ip"];
        _port = atoi(loginParam["port"].c_str());
        _username = loginParam["username"];
        _password = loginParam["password"];
        _db = loginParam["db"];
        _initSize = atoi(loginParam["initsize"].c_str());
        _maxSize = atoi(loginParam["maxSize"].c_str());
        _maxIdleTime = atoi(loginParam["maxIdleTime"].c_str());
        _connectionTimeout = atoi(loginParam["connectionTimeout"].c_str());

        return true;
    }

    // 生产连接，由生产者线程调用
    void produceConnection()
    {
        while (true)
        {
            std::unique_lock<std::mutex> lock(_queueMutex);
            while (!_connectionQueue.empty())
            {
                _cv.wait(lock);
            }

            if (_connectionCount < _maxSize)
            {
                connection *p = new connection();
                p->connect(_ip, _port, _username, _password, _db);
                p->refreshAliveTime(); // 刷新一下连接开始空闲的起始时间
                _connectionQueue.push(p);
                ++_connectionCount;
            }

            _cv.notify_all();
        }
    }

    // 扫描空连接是否超过maxIdleTime
    void scannConnection()
    {
        while (true)
        {
            // 通过 sleep 来模拟定时效果
            std::this_thread::sleep_for(std::chrono::seconds(_maxIdleTime));
            // 扫描整个队列, 释放多余的空闲连接
            std::unique_lock<std::mutex> lock(_queueMutex);
            while (_connectionCount > _initSize)
            {
                connection *p = _connectionQueue.front();
                if (p->getAliveTime() >= (_maxIdleTime * 1000))
                {
                    _connectionQueue.pop();
                    --_connectionCount;
                    delete p; // 调用 ~Connection() 释放连接
                }
                else
                {
                    break; // 队头连接都没有超过 _maxIdleTime, 其他连接肯定也没有
                }
            }
        }
    }

private:
    std::string _ip;
    uint16_t _port;
    std::string _username;
    std::string _password;
    std::string _db;
    int _initSize;          // 初始连接量
    int _maxSize;           // 最大连接量
    int _maxIdleTime;       // 允许最大空闲时间
    int _connectionTimeout; // 获取连接超时时间

    static connectionPool *_conninstance;
    static std::mutex _connMutex;

    std::queue<connection *> _connectionQueue; // 存储 MySQL 连接的队列
    std::mutex _queueMutex;                    // 保证连接队列线程安全的互斥锁
    std::atomic_int _connectionCount;          // 记录所创建的 Connection 连接的总数量
    std::condition_variable _cv;               // 设置条件变量, 用于生产者线程和消费者线程之间的同步
};

// 初始化静态成员变量
connectionPool *connectionPool::_conninstance = nullptr;
std::mutex connectionPool::_connMutex;
